﻿using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace KaroGame.FrontEnd
{
    #region Cone Type
    public enum ConeType
    {
        White,
        Red
    }
    #endregion

    public class ConeList : IConeProvider
    {
        #region Properties
        private Game game;
        private Model whiteConeModel;
        private Model redConeModel;
        private Dictionary<int, KaroCone> cones;

        private int hovered;
        private int selected;

        public KaroCone this[int tileId]
        {
            get { return cones[tileId]; }
            set { cones[tileId] = value; }
        }
        
        public int Hovered
        {
            get { return hovered; }
        }

        public int Selected
        {
            get { return selected; }
        }
        
        public List<KaroCone> Cones
        {
            get { return cones.Values.ToList(); }
        }
        #endregion

        #region Constructors
        public ConeList(Game game)
        {
            this.game = game;
            this.cones = new Dictionary<int, KaroCone>();
            this.selected = -1;
            this.hovered = -1;
        }
        #endregion

        #region Public Methods
        public void LoadContent()
        {
            this.whiteConeModel = game.Content.Load<Model>("WhiteCone");
            this.redConeModel = game.Content.Load<Model>("RedCone");
        }

        public void Add(int tileId, ConeType coneType)
        {
            KaroCone newCone = new KaroCone( game,
                                             coneType == ConeType.White ? whiteConeModel : redConeModel,
                                             coneType,
                                             tileId );
            game.Components.Add( newCone );

            cones.Add( tileId, newCone );
        }

        public KaroCone Remove( int tileId )
        {
            KaroCone removedCone = cones[tileId];

            if (removedCone != null)
            {
                Remove( removedCone );
            }

            return removedCone;
        }

        public void Remove( KaroCone cone )
        {
            game.Components.Remove( cone );

            cones.Remove( cone.TileId );
        }

        public void Clear()
        {
            foreach (KaroCone cone in cones.Values)
            {
                game.Components.Remove( cone );
            }

            cones.Clear();
        }

        public KaroCone GetById(int tileId)
        {
            return cones[tileId];
        }

        public int GetIdByIndex(int index)
        {
            return cones.Keys.ToList()[index];
        }

        public void Move(int coneId, int tileId, bool shouldBeFlipped)
        {
            GetById(coneId).MoveAnimated(tileId, shouldBeFlipped);

            // Since the cones dictionary is arranged by the tileId's the cone is on, we need to change the key
            // of the cone to be moved, in order keep the dictionary valid.
            KaroCone coneBackup = cones[coneId];
            cones.Remove( coneId );
            cones.Add( tileId, coneBackup );
        }

        public int Select(Ray mouseRay, ref float distance)
        {
            Dictionary<int, float> collisions = new Dictionary<int, float>();

            foreach(KaroCone cone in cones.Values)
            {
                float ? tempDistance = cone.Intersects( mouseRay );
                if ( tempDistance != null )
                {
                    collisions.Add( cone.TileId, tempDistance.Value );
                }
            }

            if (collisions.Count > 0)
            {
                KeyValuePair<int, float> minimumDistance = new KeyValuePair<int, float>( 0, float.MaxValue );

                foreach (KeyValuePair<int, float> keyValuePair in collisions)
                {
                    if (keyValuePair.Value < minimumDistance.Value)
                    {
                        minimumDistance = keyValuePair;
                    }
                }

                distance = minimumDistance.Value;

                return minimumDistance.Key;
            }

            distance = float.NaN;

            return -1;
        }

        public void Activate(int coneId)
        {
            if(cones.ContainsKey(coneId))
            {
                cones[coneId].Activate();
                hovered = coneId;
            }
        }

        public void Deactivate()
        {
            foreach(KaroCone c in cones.Values)
                c.Deactivate();
            hovered = -1;
        }

        public void Select(int coneId)
        {
            if (cones.ContainsKey(coneId))
            {
                cones[coneId].Select();
                selected = coneId;
            }
        }

        public void Deselect()
        {
            foreach (KaroCone c in cones.Values)
                c.Deselect();
            selected = -1;
        }
        #endregion
    }
}